/**
  初始化：const db = new JsonDB<{user: {name: string}}>('./data.json')
  设置数据：db.set('user', {name: 'Alice'})
  获取数据：db.get('user')
  异步保存：await db.save()
  删除数据：db.delete('user')
  db.update('user.profile.age', 25)
 */


import fs, { promises as fsp } from 'fs'
import path from 'path'

interface IJsonDBOptions {
  autoSave?: boolean
  pretty?: boolean
}

export class JsonDB<T extends Record<string, any> = any> {
  private data: T
  private readonly filePath: string
  private readonly options: Required<IJsonDBOptions>

  constructor(filePath: string, options: IJsonDBOptions = {}) {
    this.filePath = path.resolve(filePath)
    this.options = {
      autoSave: true,
      pretty: false,
      ...options
    }
    this.data = {} as T
    this._initSync()
  }

  private _initSync() {
    try {
      const rawData = fs.readFileSync(this.filePath, 'utf-8')
      this.data = JSON.parse(rawData) || ({} as T)
    } catch {
      this.data = {} as T
      if (this.options.autoSave) {
        this.saveSync()
      }
    }
  }

  async load(): Promise<void> {
    try {
      const rawData = await fsp.readFile(this.filePath, 'utf-8')
      this.data = JSON.parse(rawData) || ({} as T)
    } catch {
      this.data = {} as T
      if (this.options.autoSave) {
        await this.save()
      }
    }
  }

  async save(): Promise<void> {
    const content = this.options.pretty
      ? JSON.stringify(this.data, null, 2)
      : JSON.stringify(this.data)
    await fsp.writeFile(this.filePath, content)
  }

  saveSync(): void {
    const content = this.options.pretty
      ? JSON.stringify(this.data, null, 2)
      : JSON.stringify(this.data)
    fs.writeFileSync(this.filePath, content)
  }

  /**
   * 更新嵌套属性 (支持点路径)
   * @param path 属性路径 (如 'user.profile.name')
   * @param value 新值
   */
  update(path: string, value: any): void {
    const keys = path.split('.')
    let current: any = this.data

    for (let i = 0; i < keys.length - 1; i++) {
      const key = keys[i]
      if (!current[key]) {
        current[key] = {}
      }
      current = current[key]
    }

    current[keys[keys.length - 1]] = value
    if (this.options.autoSave) {
      this.saveSync()
    }
  }

  /**
   * 异步更新嵌套属性
   * @param path 属性路径
   * @param value 新值
   */
  async updateAsync(path: string, value: any): Promise<void> {
    this.update(path, value)
    if (this.options.autoSave) {
      await this.save()
    }
  }

  /**
  * 添加数据到数组或对象
  * @param path 属性路径 (如 'users.list' 或 'users.0.friends')
  * @param value 要添加的值
  * @param options 配置项
  *   - forceArray: 如果路径不存在，强制创建为数组
  */
  add(path: string, value: any, options: { forceArray?: boolean } = {}): void {
    const keys = path.split('.')
    let current: any = this.data

    for (let i = 0; i < keys.length - 1; i++) {
      const key = keys[i]
      if (!current[key]) {
        current[key] = options.forceArray ? [] : {}
      }
      current = current[key]
    }

    const lastKey = keys[keys.length - 1]
    if (Array.isArray(current)) {
      if (lastKey === '') {
        // 直接追加到数组
        current.push(value)
      } else {
        // 指定索引添加
        const index = parseInt(lastKey)
        if (!isNaN(index)) {
          current.splice(index, 0, value)
        }
      }
    } else if (current[lastKey] === undefined) {
      // 新属性
      current[lastKey] = value
    } else if (Array.isArray(current[lastKey])) {
      // 已有数组追加
      current[lastKey].push(value)
    } else {
      // 非数组属性转为数组
      current[lastKey] = [current[lastKey], value]
    }

    if (this.options.autoSave) {
      this.saveSync()
    }
  }

  /**
   * 异步添加数据
   */
  async addAsync(path: string, value: any, options: { forceArray?: boolean } = {}): Promise<void> {
    this.add(path, value, options)
    if (this.options.autoSave) {
      await this.save()
    }
  }

  get<K extends keyof T>(key: K): T[K] {
    return this.data[key]
  }

  set<K extends keyof T>(key: K, value: T[K]): void {
    this.data[key] = value
    if (this.options.autoSave) {
      this.saveSync()
    }
  }

  delete<K extends keyof T>(key: K): boolean {
    const exists = key in this.data
    delete this.data[key]
    if (exists && this.options.autoSave) {
      this.saveSync()
    }
    return exists
  }

  getAll(): T {
    return { ...this.data }
  }

  clear(): void {
    this.data = {} as T
    if (this.options.autoSave) {
      this.saveSync()
    }
  }
}
